//接收到的信息为UDP下的有效载荷,即DNS部分
#include "IPMap.h"

#define DNS_HEAD_SIZE 12  // DNS头大小
#define BUF_MAX_SIZE 1024 //缓冲区最大大小
#define ID_EXPIRE_TIME 10 //向服务器的请求超时时间,ttl单位为s

int get_url_from_question(char* buf, char* url, int debug_level)
{
    //从接收到的DNS请求中获得域名,返回域名长度,ipv6请求或出错返回-1
    //question中域名分段，每一段开头一个字节记录这一段有几个字节，00为question结尾
    int index = DNS_HEAD_SIZE, num = buf[index], url_len = 0;

    index++;
    while (true) {
        for (int i = 1; i <= num; i++, index++, url_len++)
            url[url_len] = buf[index];
        num = buf[index];
        if (num == 0) {
            url_len++;
            index++;
            break;
        }
        else {
            url[url_len] = '.';
            index++;
            url_len++;
        }
    }
    //Type为1，为ipv4，为28为ipv6
    unsigned short type = ntohs(*(unsigned short*)&buf[index]);
    if (type == 1)
        return url_len;
    else
        return -1;
}

void print_package(char *buf, int length)
{
    //输出一个数据包
    unsigned char unit;
    printf("Packet length = %d\n", length);
    printf("Details of the package:\n");
    for (int i = 0; i < length; i++)
    {
        unit = (unsigned char)buf[i];
        printf("%02x ", unit);
    }
    printf("\n");
    return;
}

int write_package(char *sendbuf, char *buf, int length, char* ip, int debug_level)
{
    //编写一个数据包,返回其长度,一些长度居然百度结果不一样。。。我就自己抓了下包
    /* 头部分 */
    //请求数据包的ID等赋值给返还结果
    memcpy(sendbuf, buf, length);
    // QR为1,表示响应报；OPCODE为0,表示标准查询；RCODE为0,表示没有差错
    unsigned short a = htons(0x8180);//1 0000 0011 000 0000
    memcpy(&sendbuf[2], &a, sizeof(unsigned short));
    /* 拦截不良网站 */
    if (strcmp(ip,"0.0.0.0")==0)
        a = htons(0x0000);
    else
        a = htons(0x0001);
    // Answers数置为0
    memcpy(&sendbuf[6], &a, sizeof(unsigned short));
    /* 查询结果部分 */
    int curLen = 0;
    // answer长度2+2+2+4+2+4=16B
    char answer[16];
    //域名字段,长度2B
    unsigned short Name = htons(0xc00c);//11 00 0000 0000 1100移指针，指向Question的域名
    memcpy(answer, &Name, sizeof(unsigned short));
    curLen += sizeof(unsigned short);
    //类型字段,为Type A,长度2B
    unsigned short TypeA = htons(0x0001);//IPv4
    memcpy(answer + curLen, &TypeA, sizeof(unsigned short));
    curLen += sizeof(unsigned short);
    //类字段,为1,长度2B
    unsigned short ClassA = htons(0x0001);//Internet
    memcpy(answer + curLen, &ClassA, sizeof(unsigned short));
    curLen += sizeof(unsigned short);
    //生存期字段,长度4B
    unsigned int timeLive = htonl(0x7b);//123
    memcpy(answer + curLen, &timeLive, sizeof(unsigned int));
    curLen += sizeof(unsigned int);
    //数据长度(这里数据为IP地址,为4B)其长度为2B
    unsigned short IPLen = htons(0x0004);
    memcpy(answer + curLen, &IPLen, sizeof(unsigned short));
    curLen += sizeof(unsigned short);
    //数据字段,这里为IP地址,其长度为4B
    unsigned int IP = (unsigned int)inet_addr(ip);
    memcpy(answer + curLen, &IP, sizeof(unsigned int));
    curLen += sizeof(unsigned int);
    curLen += length;
    memcpy(sendbuf + length, answer, sizeof(answer));
    //返回数据包的长度即为请求数据包的长度加上answer的长度
    return curLen;
}

void get_ip_from_answer(char *buf,char* ip,int debug_level)
{
    //从返回结果中获得ip地址
    int nquery = ntohs(*((unsigned short *)(buf + 4))), nresponse = ntohs(*((unsigned short *)(buf + 6)));
    char *p = buf + 12;
    int ip1, ip2, ip3, ip4;
    /* 一直位移指针,直到到达Type A的answer,即包含ip地址的answer */
    //经过question部分
    for (int i = 0; i < nquery; i++)
    {
        //大于零的时候为域名部分
        while (*p > 0)
            p += (*p) + 1;
        //从零开始表示域名结束，加上type（2B）与class（2B）为五个字节
        p += 5;
    }
    for (int i = 0; i < nresponse; ++i)
    {
        //经过Name部分
        //if ((unsigned char)*p == 0xc0)//bug
        if((((unsigned char)*p) & 0xc0) == 0xc0)//如果是偏移指针
            p += 2;
        else//是域名
        {
            while (*p > 0)
                p += (*p) + 1;
            ++p;
        }
        // 经过Type（2B） Class（2B）TTL（4B）数据长度（4B）到达数据部分
        unsigned short resp_type = ntohs(*(unsigned short *)p); 
        p += 2;
        unsigned short resp_class = ntohs(*(unsigned short *)p); 
        p += 2;
        unsigned int TTL = ntohs(*(unsigned int *)p); 
        p +=4;
        int datalen = ntohs(*(unsigned short *)p); 
        p += 2;
        if (debug_level >= 2)
            printf("Type -> %d,  Class -> %d,  TTL -> %d\n", resp_type, resp_class, TTL);
        //不是IP地址跳过该数据部分
        if (resp_type != 1)
            p += datalen;
        /* 达到包含IP地址的位置 */
        else
        {
            ip1 = (unsigned char)*p++;
            ip2 = (unsigned char)*p++;
            ip3 = (unsigned char)*p++;
            ip4 = (unsigned char)*p++;
            sprintf(ip, "%d.%d.%d.%d", ip1, ip2, ip3, ip4);
            break;
        }
    }
    return;
}